#ifndef WKT_GRAMMAR_H
#define WKT_GRAMMAR_H

#ifdef Gocad_utils_enter_scope_h
#error Please include all boost depending headers before GOCAD headers!
#endif

#include "GrammarRawStructs.h"
#include "IGeometryActions.h"
#include "Parsers/GrammarErrorHandler.hpp"

//#include <boost/config/warning_disable.hpp>
#include <boost/spirit/include/qi.hpp>
#include <boost/spirit/include/phoenix.hpp>
#include <boost/function.hpp>
#include <boost/bind.hpp>
#include <boost/shared_ptr.hpp> 

#include <iostream>

namespace spirit = boost::spirit;
namespace qi	= boost::spirit::qi;
namespace ascii = boost::spirit::ascii;
namespace phoenix = boost::phoenix;


namespace GST{
namespace Parsers{

//
// the grammar
//
template <typename Iterator, typename Skipper>
class WKTGrammar3 : public qi::grammar<Iterator, Skipper>
{
public:
    WKTGrammar3(boost::shared_ptr<IGeometryActions> actions) 
		: WKTGrammar3::base_type(geometry_z_tagged_text_) //define here the base rule
		, _actions(actions)
    {
		using qi::double_;
		using qi::uint_;
		using qi::lit;
		using qi::no_case;
		using qi::eps;
		using qi::ulong_;

		//define here function pointers to Semantic actions (more beautiful grammar definiton)
		typedef boost::function< void( const RawPoint3&, qi::unused_type, qi::unused_type  ) >		point_action;
		typedef boost::function< void( const IdxTriangle&, qi::unused_type, qi::unused_type  ) >	triangle_action;
		typedef boost::function< void( const IdxTetrahedron&, qi::unused_type, qi::unused_type  ) >	tetrahedron_action;
		typedef boost::function< void( qi::unused_type, qi::unused_type, qi::unused_type  ) >		void_action;

		point_action	addPoint(		boost::bind(&IGeometryActions::addPoint				, _actions, ::_1 ) );		
		void_action		newSimplex(		boost::bind(&IGeometryActions::newSimplex			, _actions ) );
		void_action		createPoint3(	boost::bind(&IGeometryActions::CreateMultipoint3	, _actions ) );
		void_action		createMultiLine3(	boost::bind(&IGeometryActions::CreateMultiline3	, _actions ) );
		void_action		createMultiPolygon3(boost::bind(&IGeometryActions::CreateMultiPolygon3, _actions ) );
		void_action		createPolyhedralSurf3(boost::bind(&IGeometryActions::CreateMultiPolygon3, _actions ));
		void_action		createTIN3(boost::bind(&IGeometryActions::CreateTIN3				, _actions ));
		void_action		createGeomCollection(boost::bind(&IGeometryActions::CreateCollection, _actions ));
		void_action		createTN3(boost::bind(&IGeometryActions::CreateTriangleNet3			, _actions ));
		void_action		createTetrahedon3(boost::bind(&IGeometryActions::CreateTetrahedon3	, _actions ));		
		triangle_action addTriangle(		boost::bind(&IGeometryActions::addTriangle		, _actions, ::_1 ) );		
		tetrahedron_action addTetrahedron(	boost::bind(&IGeometryActions::addTetrahedron	, _actions, ::_1 ) );		
		

		//
		//###  the grammar definiton   ###

		point_z_									/*SFS conform: OGC 06-103r4*/
			= double_		/*x*/
			> double_		/*y*/
			> double_		/*z*/			
		;

		triangle_point_index_
			= ulong_			/*v1*/
			> ulong_			/*v2*/
			> ulong_			/*v3*/
		;

		tetrahedron_point_index_
			= ulong_			/*v1*/
			> ulong_			/*v2*/
			> ulong_			/*v3*/
			> ulong_			/*v4*/
		;

		triangle_point_index_list_
			= triangle_point_index_[addTriangle]
			> *( lit(',') > triangle_point_index_[addTriangle] )
		;

		tetrahedron_point_index_list_
			= tetrahedron_point_index_[addTetrahedron]
			> *( lit(',') > tetrahedron_point_index_[addTetrahedron] )
		;

		point_z_text_								/*SFS conform: OGC 06-103r4*/
			= lit('(')
			> point_z_[addPoint]		/*create a point from rule*/
			> lit(')')
		;

		multipoint_z_text_							/*SFS conform: OGC 06-103r4*/
			= lit('(')
			> point_z_text_
			> *( lit(',') > point_z_text_ )
			> lit(')')
		;

		linestring_z_text_							/*SFS conform: OGC 06-103r4*/
			= lit('(')
			> point_z_[addPoint]
			> *( lit(',') > point_z_[addPoint] ) /*create a point from rule*/
			> lit(')')
		;

		multilinestring_z_text_						/*SFS conform: OGC 06-103r4*/
			= lit('(')
			> eps[newSimplex]										/*release newPart before addPoints*/
			> linestring_z_text_
			> *( lit(',') > eps[newSimplex] > linestring_z_text_ ) /*release newPart before addPoints*/
			> lit(')')
		;

		polygon_z_text_								/*SFS conform: OGC 06-103r4*/
			= lit('(')
			> linestring_z_text_
			> *( lit(',') > linestring_z_text_ ) /*release newPart before addPoints*/
			> lit(')')
		;

		multipolygon_z_text_						/*SFS conform: OGC 06-103r4*/
			= lit('(')
			> eps[newSimplex]						/*release newPart before addPoints*/
			> polygon_z_text_				
			> *( lit(',') 
						> eps[newSimplex]
						> polygon_z_text_ )		/*release newPart before addPoints*/
			> lit(')')
		;

		trianglenet_z_
			= lit('(')
			> linestring_z_text_  /*is a point_coordinate_list*/
			> lit(',')
			> triangle_point_index_list_
			> lit(')')
		;

		tetrahedronnet_z_
			= lit('(')
			> linestring_z_text_  /*is a point_coordinate_list*/
			> lit(',')
			> tetrahedron_point_index_list_
			> lit(')')
		;

		geometrycollection_z_text_					/*SFS conform: OGC 06-103r4*/
			= lit('(')
			> geometry_z_tagged_text_			/*create new geometries recursively*/
			> *( lit(',') > geometry_z_tagged_text_ )
			> lit(')')
		;

		point_z_tagged_text_						/*SFS conform: OGC 06-103r4*/
			= no_case["point z"]
			> eps[createPoint3]			/*'point z' matched, so create an object*/
			> point_z_text_
		;

		multipoint_z_tagged_text_					/*SFS conform: OGC 06-103r4*/
			= no_case["multipoint z"]
			> eps[createPoint3]			/*'multipoint z' matched, so create an object*/
			> multipoint_z_text_
		;

		linestring_z_tagged_text_					/*SFS conform: OGC 06-103r4*/
			= no_case["linestring z"]
			> eps[createMultiLine3]		/*'multilinestring z' matched, so create an object*/
			> eps[newSimplex]				/*relase newSimplex() */
			> linestring_z_text_
		;

		polygon_z_tagged_text_						/*SFS conform: OGC 06-103r4*/
			= no_case["polygon z"]
			> eps[createMultiPolygon3]	/*'polygon z' matched, so create an object*/
			> eps[newSimplex]				/*relase newSimplex() */
			> polygon_z_text_
		;

		multipolygon_z_tagged_text_					/*SFS conform: OGC 06-103r4*/
			= no_case["multipolygon z"]
			> eps[createMultiPolygon3]			/*'polygon z' matched, so create an object*/
			> multipolygon_z_text_
		;	

		multilinestring_z_tagged_text_				/*SFS conform: OGC 06-103r4*/
			= no_case["multilinestring z"]
			> eps[createMultiLine3]				/*'multilinestring z' matched, so create an object*/
			> multilinestring_z_text_
		;

		polyhedralsurface_z_tagged_text_			/*SFS conform: OGC 06-103r4*/
			= no_case["polyhedralsurface z"]
			> eps[createPolyhedralSurf3]				/*'polyhedralsurface z' matched, so create an object*/
			> multipolygon_z_text_
		;

		triangle_tagged_text_						/*SFS conform: OGC 06-103r4*/
			= no_case["triangle z"]
			> eps[createTIN3]				/*'triangle z' matched, so create an object*/
			> eps[newSimplex]				/*relase newSimplex() */
			> polygon_z_text_
		;

		tin_tagged_text_							/*SFS conform: OGC 06-103r4*/
			= no_case["tin z"]
			> eps[createTIN3]				/*'tin z' matched, so create an object*/
			> multipolygon_z_text_
			//> polyhedralsurface_z_text_
		;

		trianglenet_tagged_text_
		= no_case["trianglenet z"]
			> eps[createTN3]				/*'trianglenet z' matched, so create an object*/
			> trianglenet_z_
		;

		tetrahedronnet_tagged_text_
		= no_case["tetrahedronnet z"]
			> eps[createTetrahedon3]				/*'tetrahedronnet z' matched, so create an object*/
			> tetrahedronnet_z_
		;

		geometrycollection_z_tagged_text_			/*SFS conform: OGC 06-103r4*/
			= no_case["geometrycollection z"]
			> eps[createGeomCollection]		/*'geometrycollection z' matched, so create an object*/
			> geometrycollection_z_text_
		;

		geometry_z_tagged_text_ 
			= point_z_tagged_text_					/*SFS conform: OGC 06-103r4*/
			| linestring_z_tagged_text_				/*SFS conform: OGC 06-103r4*/
			| polygon_z_tagged_text_				/*SFS conform: OGC 06-103r4*/
			| polyhedralsurface_z_tagged_text_		/*SFS conform: OGC 06-103r4*/
			| triangle_tagged_text_					/*SFS conform: OGC 06-103r4*/
			| tin_tagged_text_						/*SFS conform: OGC 06-103r4*/
			| multipoint_z_tagged_text_				/*SFS conform: OGC 06-103r4*/
			| multilinestring_z_tagged_text_		/*SFS conform: OGC 06-103r4*/
			| multipolygon_z_tagged_text_			/*SFS conform: OGC 06-103r4*/
			| geometrycollection_z_tagged_text_		/*SFS conform: OGC 06-103r4*/
			| trianglenet_tagged_text_				/*this is our TIN; less memory consuming*/
			| tetrahedronnet_tagged_text_			/*this is our multipolygon; less memory consuming*/
		;
		//### end of grammar definiton ###
		//

		//
		//### error handling
		geometry_z_tagged_text_.name(				"geometry_z_tagged_text_");
		point_z_tagged_text_.name(					"point_z_tagged_text_");
		multipoint_z_tagged_text_.name(				"multipoint_z_tagged_text_");
		multipoint_z_text_.name(					"multipoint_z_text_");
		multilinestring_z_tagged_text_.name(		"multilinestring_z_tagged_text_");
		multilinestring_z_text_.name(				"multilinestring_z_text_");
		polygon_z_tagged_text_.name(				"polygon_z_tagged_text_");
		polygon_z_text_.name(						"polygon_z_text_");
		multipolygon_z_tagged_text_.name(			"multipolygon_z_tagged_text_");
		multipolygon_z_text_.name(					"multipolygon_z_text_");
		linestring_z_tagged_text_.name(				"linestring_z_tagged_text_");
		linestring_z_text_.name(					"linestring_z_text_");
		point_z_text_.name(							"point_z_text_");
		point_z_.name(								"point_z_");
		polyhedralsurface_z_tagged_text_.name(		"polyhedralsurface_z_tagged_text_");
		polyhedralsurface_z_text_.name(				"polyhedralsurface_z_text_");
		triangle_tagged_text_.name(					"triangle_tagged_text_");
		tin_tagged_text_.name(						"tin_tagged_text_");
		geometrycollection_z_tagged_text_.name(		"geometrycollection_z_tagged_text_");
		geometrycollection_z_text_.name(			"geometrycollection_z_text_");
		trianglenet_tagged_text_.name(				"trianglenet_tagged_text_");
		tetrahedronnet_tagged_text_.name(			"tetrahedronnet_tagged_text_");	
		trianglenet_z_.name(						"trianglenet_z_");
		tetrahedronnet_z_.name(						"tetrahedronnet_z_");	
		triangle_point_index_list_.name(			"triangle_point_index_list_");
		tetrahedron_point_index_list_.name(			"tetrahedron_point_index_list_");
		triangle_point_index_.name(					"triangle_point_index_");
		tetrahedron_point_index_.name(				"tetrahedron_point_index_");

		

		namespace qil = qi::labels;

		qi::on_error<qi::fail>(geometry_z_tagged_text_,
							   error_handler_func(error_handler())(
									   qil::_1, qil::_2, qil::_3, qil::_4));
		//### end of error handling
		//

    } //end of c'tor

	qi::rule<Iterator, Skipper> geometry_z_tagged_text_;
	qi::rule<Iterator, Skipper> point_z_tagged_text_;
	qi::rule<Iterator, Skipper> point_z_text_;
	qi::rule<Iterator, RawPoint3(), Skipper> point_z_;
	qi::rule<Iterator, Skipper> multipoint_z_tagged_text_;
	qi::rule<Iterator, Skipper> multipoint_z_text_;
	qi::rule<Iterator, Skipper> multilinestring_z_tagged_text_;
	qi::rule<Iterator, Skipper> multilinestring_z_text_;
	qi::rule<Iterator, Skipper> polygon_z_tagged_text_;
	qi::rule<Iterator, Skipper> polygon_z_text_;
	qi::rule<Iterator, Skipper> multipolygon_z_tagged_text_;
	qi::rule<Iterator, Skipper> multipolygon_z_text_;
	qi::rule<Iterator, Skipper> linestring_z_tagged_text_;
	qi::rule<Iterator, Skipper> linestring_z_text_;
	qi::rule<Iterator, Skipper> polyhedralsurface_z_tagged_text_;
	qi::rule<Iterator, Skipper> polyhedralsurface_z_text_;
	qi::rule<Iterator, Skipper> triangle_tagged_text_;
	qi::rule<Iterator, Skipper> tin_tagged_text_;
	qi::rule<Iterator, Skipper> geometrycollection_z_tagged_text_;
	qi::rule<Iterator, Skipper> geometrycollection_z_text_;
	qi::rule<Iterator, Skipper> trianglenet_tagged_text_;
	qi::rule<Iterator, Skipper> trianglenet_z_;
	qi::rule<Iterator, Skipper> tetrahedronnet_tagged_text_;
	qi::rule<Iterator, Skipper> tetrahedronnet_z_;
	qi::rule<Iterator, Skipper> triangle_point_index_list_;
	qi::rule<Iterator, Skipper> tetrahedron_point_index_list_;
	qi::rule<Iterator, IdxTriangle()	, Skipper> triangle_point_index_;
	qi::rule<Iterator, IdxTetrahedron()	, Skipper> tetrahedron_point_index_;
	

private:
	boost::shared_ptr<IGeometryActions> _actions;
};

}// namespace Parsers
}// namespace GST

#endif //WKT_GRAMMAR_H